import com.crosbymichael.filesystem.ReadSDKPath;
import com.crosbymichael.filesystem.WriteSDKPath;
import com.crosbymichael.os.GetOperatingSystem;
import com.crosbymichael.parser.RemoveExtension;
import com.crosbymichael.process.FileExtention;
import com.crosbymichael.process.PackageEvent;
import com.crosbymichael.process.PackageNativeInstaller;

import flash.desktop.NativeApplication;
import flash.events.Event;
import flash.events.FileListEvent;
import flash.filesystem.File;

import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.utils.object_proxy;

/**
 * Variable to store that path to the adt file after it is read on the 
 * initial startup run. 
 */
private var _sdkPath:String;

/**
 * Path to save the native installers after they are packaged. 
 */
private var _outputPath:String;

/**
 * Dock icon for osx systems. 
 */
[Embed(source="assets/icon_128.png")]
private var dockIcon:Class;

/**
 * How many files in the array to be packaged. 
 */
private var _fileCount:int;

/**
 * Array that holds the file name and path information needed.
 * It is also the data provider for the UI list that is displayed.
 * Bindable to display changes to the list. 
 */
[Bindable]
public var filesToBePackaged:ArrayCollection = new ArrayCollection();

/**
 * Output Text for the textarea display output. 
 */
[Bindable]
private var outputText:String;

/**
 * Inital function on creation of MainWindow 
 * 
 */
private function init():void {
	//Set dropdown list visibility
	setArchVisability();
	//Get path to sdk and store in memory.
	readSDKPath();
	//Create a dock icon for osx
	dockIconCreate();
	setInitOutputText();
}

/**
 * Sets the intial text for the output display. 
 * 
 */
private function setInitOutputText():void
{
	var d:Date = new Date();
	outputText = String(d) + "\n" + "Welcome to Air Native Packager \n";
}


/**
 * Check the current os and if it is linux,
 * sets the dropdown list to visible. 
 * 
 */
private function setArchVisability():void {
	if (GetOperatingSystem.getOS() == "lin") {
		linuxArch.visible = true;
	}
}

/**
 * Creates a dock icon for osx systems. 
 * 
 */
private function dockIconCreate():void {
	//Only create icon if the system supports it.
	if (NativeApplication.supportsDockIcon)
	{
		NativeApplication.nativeApplication.icon.bitmaps = 
			[new dockIcon().bitmapData];
	}
}

/**
 * Uses the class to read the sdk's nativePath,
 * if it returns blank, calls function to have the user browse for 
 * the sdk adt's location. 
 * 
 */
private function readSDKPath():void {
	var read:ReadSDKPath = new ReadSDKPath();
	if (read.readPath() != "") {
		_sdkPath = read.readPath();
	} else {
		locateSDK();		
	}
}

/**
 * Uses a file object and the event listener with a
 * native browse for file window to locate the sdk's 
 * adt file. 
 * 
 */
private function locateSDK():void {
	startSearch();
}

/**
 * Creates the file obj and events. 
 * 
 */
private function startSearch():void
{
	var f:File = new File();
	f.addEventListener(Event.SELECT, sdkLocated);
	f.addEventListener(Event.CANCEL, locateCanceled);
	f.browseForOpen("Find adt in your Air SDK's bin folder");
}

/**
 * Incase the user cancles the location process.
 *  
 * @param e
 * 
 */
private function locateCanceled(e:Event):void {
	e.stopPropagation();
	Alert.show("You must find the location of adt.  Try again?", 
		"Failed to locate adt.", Alert.YES | Alert.NO, this, locateFailedAlert);
}

/**
 * If alert, yes, recall locateSDK, if no, Exit App.  We are not playing around
 * I want to know where adt is. 
 * @param e
 * 
 */
private function locateFailedAlert(e:CloseEvent):void {
	if (e.detail == Alert.YES) {
		startSearch();
	} else {
		//Exit app, i wasn't joking.
		NativeApplication.nativeApplication.exit();
	}
}

/**
 * Event handler for the browes for open file operation.
 *  
 * @param e Event obj that holds the nativePath to the adt file.
 * 
 */
private function sdkLocated(e:Event):void {
	var write:WriteSDKPath = new WriteSDKPath();
	write.writePath(e.target.nativePath);
	
	//Clean up event listener
	e.target.removeEventListener(Event.SELECT, sdkLocated);
}


/**
 * Browse for multiple files and add then to the list
 * for packaging. 
 * 
 */
private function startAddProcess():void {
	var f:File = new File();
	f.addEventListener(FileListEvent.SELECT_MULTIPLE, multipleAirFileSelected);
	//Only allow the user to open .air files.
	f.browseForOpenMultiple("Select .air files to add then to the que.");
}

/**
 * Evnet when the user selects air files to package.
 * Need to check if it is just one file or many.
 *  
 * @param e Select event obj for browseForOpenMultiple
 * 
 */
private function multipleAirFileSelected(e:FileListEvent):void
{
	if (e.files.length > 1) {
		//Multiple files in the file array.
		for (var i:int = 0; i < e.files.length; i++) {
			filesToBePackaged.addItem({filename: e.files[i].name, nativePath: e.files[i].nativePath});		
		}	
	} else {
		//Only a single files was selected.
		//Add validation to make sure files are .air extension
		filesToBePackaged.addItem({filename: e.files[0].name, nativePath: e.files[0].nativePath});
	}
}

/**
 * First, locate a folder to save the native installers to,
 * package the .air files, save them, and open folder to where
 * the installers are.  
 * 
 * P.S. for multiple files, we are going to package them all in
 * the same time. No one right after the other, we want speed.
 * 
 */
private function startPackageProcess():void {
	//See how many files are being packaged.
	_fileCount = getCount();
	
	//Checks to see if the dropdown list is visible
	if (linuxArch.visible) {
		//If it is visible, make sure that it is selected.
		if (linuxArch.selectedIndex > -1) {
			var f:File = new File();
			f.addEventListener(Event.SELECT, browseForSave);
			f.browseForDirectory("Where do you want to save the native installers?");		
		} else {
			Alert.show("You need to select your linux system type, deb or rpm", 
				"Need selection", Alert.OK, this);
		}
	} else { //This is for non-linux systems that do not have the dropdown visible.
		var f:File = new File();
		f.addEventListener(Event.SELECT, browseForSave);
		f.browseForDirectory("Where do you want to save the native installers?");
	}
	
}

/**
 * Event for when the users picks where they want the files to be saved after 
 * the native packaging process. 
 * @param e
 * 
 */
private function browseForSave(e:Event):void
{
	if (GetOperatingSystem.getOS() == "win") {
		_outputPath = e.target.nativePath + "\\";	
		processFiles();
	} else {
		_outputPath = e.target.nativePath + "/";	
		processFiles();
	}
}

/**
 * Count how many items are in the array to be packaged. 
 * @return 
 * 
 */
private function getCount():int {
	return filesToBePackaged.length;
}

/**
 * This function ittorates though the populated array of air files and calls
 * the packageFiles function to start the processes in their own thread. 
 * 
 */
private function processFiles():void {
	for (var i:int = 0; i < filesToBePackaged.length; i++) 
	{	
		packageFiles(filesToBePackaged[i].nativePath, filesToBePackaged[i].filename);
		outputText += "Packaging " + filesToBePackaged[i].filename + "\n";
	}
	
}

/**
 * Creates an instance of the class that handles the native process.
 *  
 * @param file nativePath of the file that is to be packaged.
 * 
 */
private function packageFiles(file:String, name:String):void {
	var packageInstallers:PackageNativeInstaller = new PackageNativeInstaller();
	
	//Add event listeners for complete and errors.
	packageInstallers.addEventListener(PackageEvent.ERROR, packageError);
	packageInstallers.addEventListener(PackageEvent.FILEERROR, fileError);
	packageInstallers.addEventListener(PackageEvent.NATIVEPROCESSERROR, nativeProcessError);
	packageInstallers.addEventListener(PackageEvent.NATIVEEXIT, nativeExit);
	
	//Call packaging method
	packageInstallers.packageApp(_sdkPath, file, RemoveExtension.remove(name), _outputPath, getExtension());
}

/**
 * Marks the end for the native process 
 * @param e
 * 
 */
private function nativeExit(e:PackageEvent):void
{
	outputText += e.filename + " Installer Packaged!\n";
	_fileCount -= 1;
	//Clean out the array
	if (_fileCount < 1) {
		removeArrayItems();	
	}
	
}

private function removeArrayItems():void
{
	filesToBePackaged.removeAll();
}

private function getExtension():String {
	return FileExtention.extension(GetOperatingSystem.getOS());
}

/**
 * Standard error event for the PackageNativeInstaller class.
 *  
 * @param e
 * 
 */
private function packageError(e:PackageEvent):void
{
	trace('package error' + e.error);
}

/**
 * File error when a file is not found.
 *  
 * @param e
 * 
 */
private function fileError(e:PackageEvent):void
{
	trace('package error' + e.error);
}

/**
 * Native process error event.
 *  
 * @param e
 * 
 */
private function nativeProcessError(e:PackageEvent):void
{
	trace('package error' + e.error);	
}
